
#include <BFile>
#include <BFolder>
#include <BLibrary>
#include <BReadWrite>
#include "IReader.h"
#include "IWriter.h"

using namespace BWE;

//"get" interface function name for .brw file
typedef BReadWrite* (*func_get)();

class bplugin
{
public:
	BString				ext;
	BReadWriteHolder	rw;
	BLibraryHolder		lib;
};
BList<bplugin>		Plugins;
IReader				reader;
IWriter				writer;
extern BArray<BString> app_pluginPaths;

BWEAPI bool SetReadWrite(const BString& ext, BReadWrite* rw)
{
	if(rw == 0)
		return false;
	for (auto it = Plugins.begin(); it != Plugins.end(); ++it)
	{
		bplugin& plugin = *it;
		if (plugin.ext == ext)
		{
			plugin.lib = 0;
			plugin.rw = rw;
			return true;
		}
	}
	bplugin plgin;
	plgin.rw = rw;
	plgin.ext = ext;
	Plugins.append(plgin);
	return true;
}
BWEAPI BReadWrite* GetReadWrite(const BString& ext)
{
	if (ext.empty())
		return 0;

	//if plugin has loaded
	for (auto it = Plugins.begin(); it != Plugins.end(); ++it)
	{
		bplugin& plugin = *it;
		if (plugin.ext == ext)
		{
			return plugin.rw.ptr();
		}
	}

	BString pluginName(ext);
#ifdef _DEBUG_
	pluginName += 'd';
#endif
	pluginName += ".brw";

	BString pluginPath;
	//try default path, if don't work then try others.
	for (int i = 0; i < app_pluginPaths.size(); i++)
	{
		BString fileName = app_pluginPaths[i];
		fileName << '/' << pluginName;
		if (BFile::Exists(fileName))
		{
			pluginPath = fileName;
			break;
		}
	}

	if (pluginPath.empty())
	{
		BString fileName = getenv("BWE_PATH");
		fileName << "plugin/" << pluginName;
		if (BFile::Exists(fileName))
			pluginPath = fileName;
	}

	//try default path.
	if (pluginPath.empty())
	{
		BString fileName("./plugin/");
		fileName << pluginName;
		if (BFile::Exists(fileName))
			pluginPath = fileName;
	}

	if(pluginPath.empty())
		return 0;

	//try load plugin
	BLibraryHolder lib = new BLibrary(pluginPath);
	if (!lib->load())
		return 0;

	func_get get = (func_get)lib->get("get");
	if (!get)
		return 0;

	BReadWrite* rw = get();
	if(rw == 0)
		return 0;

	bplugin plugin;
	plugin.rw = rw;
	plugin.lib = lib;
	plugin.ext = ext;
	Plugins.append(plugin);
	return rw;
}

BWEAPI BObject* BWE::ReadObject(const BString& fileName)
{
	BString ext = fileName.ext();
	ext.lower();
	BReadWrite* rw = GetReadWrite(ext);
	if (rw)
	{
		return rw->readObject(fileName);
	}
	else
	{
		return reader.readObject(fileName);
	}
	return 0;
}
BWEAPI bool BWE::WriteObject(const BString& fileName, const BObject* object)
{
	BString ext = fileName.ext();
	ext.lower();
	BReadWrite* rw = GetReadWrite(ext);
	if (rw)
	{
		return rw->writeObject(object, fileName);
	}
	else
	{
		return writer.writeObject(object, fileName);
	}
	return false;
}

